﻿// FaceCheckSample.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <WinSock2.h>
#include <windows.h>
#include <map>
#include <vector>
#include <io.h>
#include "json/json.h"
#include "MD5Checksum.h"
//引入Poco
#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPResponse.h"
#include "Poco/Net/HTMLForm.h"
#include "Poco/StreamCopier.h"
#include "Poco/Net/HTTPSClientSession.h"
#include "Poco/net/HTTPClientSession.h"
#include "Poco/Net/InvalidCertificateHandler.h"
#include "Poco/Net/AcceptCertificateHandler.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/FilePartSource.h"
#include "Poco/URI.h"

#pragma comment(lib,"iphlpapi.lib")

using namespace std;
using namespace Poco;
using namespace Poco::Net;
using Poco::StreamCopier;
using Poco::Thread;

static unsigned char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
typedef std::map<std::string, std::string> MHeaderParam;

const string c_strIP = "www.4wgj.com";//能力云接口IP地址
const unsigned short c_uPort = 443;   //能力云接口端口号

//需要替换为自己的能力云账号
const string c_strClientID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const string c_strClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const string c_strUserID = "xxxxxxxxxx";

enum EMFaceCheckResult
{
    emFaceCheckPass = 0,       //比对成功
    emFaceCheckFail,           //比对失败
    emFaceCheckError = 1001,   //请求失败
    emFaceCheckIllegalFactory, //非法厂商
    emFaceCheckIllegalInfo,    //信息非法
    emFaceCheckFileNotFound,   //找不到文件
    emFaceCheckErrorKey,       //appid或appsecret错误
    emFaceCheckErrorJson,      //json错误
};


//获取文件base64 URL编码数据
string GetPhotoString(const char *pPhotoPath);
char* Utf8ToAnsi(const char *pchUtf8);
//获取能力云token
bool NengLiYunGetToken(const char* pAppID, const char* pAppSecret, char* pToken);
//图片一比一接口
int NengLiYunFaceCheck(
    const char* pName,
    const char* pCertNum,
    const char* pCertPhoto,
    const char* pFacePhoto,
    const char* pAppID,
    const char* pAppSecret
);
/**
 * @brief http/https post接口
 *
 * @param strHost 主机ip
 * @param uPort 主机端口
 * @param strUrl 主机url路径
 * @param strData post数据，如果bUseForm为true，则以表单形式提交数据，格式数据是Json
 * {"text":{"key1":"value1","key2":"value2"},"file":{"file1":"path1","file2":"path2"}}
 * 如果没有text对象或者没有file对象，可以不传
 * @param mapHeaders http请求头参数键值对
 * @param strResponse 回应信息，如果接口返回false，此参数为错误信息
 * @param bUseSSL是否使用https访问，默认否
 * @param bUseForm是否使用表单，默认否
 * @return true
 * @return false
 */
bool DoHttpPost(
    const std::string &strHost,
    unsigned short uPort,
    const std::string &strUrl,
    const std::string &strData,
    const std::map<std::string, std::string> &mapHeaders,
    std::string &strResponse,
    bool bUseSSL = false,
    bool bUseForm = false);

//base64转码
int base64_encode(unsigned char const* pEncodeString, unsigned int in_len, unsigned char *pOutputString)
{
    int i = 0, j = 0;
    unsigned char char_array_3[3], char_array_4[4];
    unsigned char *pOutPtr = pOutputString;

    while (in_len--)
    {
        char_array_3[i++] = *(pEncodeString++);
        if (i == 3)
        {
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] = char_array_3[2] & 0x3f;

            for (i = 0; (i < 4); i++)
            {
                pOutPtr[0] += base64_chars[char_array_4[i]];
                pOutPtr++;
            }
            i = 0;
        }
    }

    if (i)
    {
        for (j = i; j < 3; j++)
            char_array_3[j] = '\0';

        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
        char_array_4[3] = char_array_3[2] & 0x3f;

        for (j = 0; (j < i + 1); j++)
        {
            pOutPtr[0] += base64_chars[char_array_4[j]];
            pOutPtr++;
        }

        while ((i++ < 3))
        {
            pOutPtr[0] += '=';
            pOutPtr++;
        }

    }
    return pOutPtr - pOutputString;
}

char* Utf8ToAnsi(const char *pchUtf8)
{
    int uSize = 0;
    WCHAR *pwText = NULL;
    char *pchAnsic = NULL;
    int ansicStrLen = 0;

    if (NULL == pchUtf8)
    {
        return NULL;
    }

    do
    {
        uSize = MultiByteToWideChar(CP_UTF8, 0, pchUtf8, -1, NULL, 0);
        if (uSize <= 0)
        {
            break;
        }
        pwText = (wchar_t*)malloc((uSize + 1) * sizeof(wchar_t));
        if (!pwText)
        {
            break;
        }
        memset((void*)pwText, 0, sizeof(wchar_t)*(uSize + 1));
        MultiByteToWideChar(CP_UTF8, 0, pchUtf8, -1, pwText, uSize);

        //widechar to utf8
        ansicStrLen = WideCharToMultiByte(CP_ACP, 0, pwText, -1, NULL, 0, NULL, NULL);
        if (0 >= ansicStrLen)
        {
            break;
        }

        pchAnsic = (char *)malloc(ansicStrLen + 2);
        if (NULL == pchAnsic)
        {
            break;
        }
        memset(pchAnsic, 0, ansicStrLen + 2);

        ansicStrLen = WideCharToMultiByte(CP_ACP, 0, pwText, -1, pchAnsic, ansicStrLen, NULL, NULL);
        if (0 >= ansicStrLen)
        {
            free(pchAnsic);
            pchAnsic = NULL;
            break;
        }

    } while (0);

    if (NULL != pwText)
    {
        free(pwText);
    }

    return pchAnsic;

}
std::string GBKToUTF8(const std::string & strGBK)
{
    int nwLen = ::MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);
    wchar_t * pwBuf = new wchar_t[nwLen + 1];//一定要加1，不然会出现尾巴 
    ZeroMemory(pwBuf, nwLen * 2 + 2);
    ::MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), strGBK.length(), pwBuf, nwLen);
    int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
    char * pBuf = new char[nLen + 1];
    ZeroMemory(pBuf, nLen + 1);
    ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
    std::string retStr(pBuf);
    delete[]pwBuf;
    delete[]pBuf;
    pwBuf = NULL;
    pBuf = NULL;
    return retStr;
}

//获取文件base64 URL编码数据
string GetPhotoString(const char *pPhotoPath)
{
    int dwUserFileSize = 0;
    char *pOrgData = NULL;
    char *pEncodeData = NULL;
    string strUrlEncode = "data:image/png;base64,"; //base64编码的header
    try
    {
        ifstream objFile(pPhotoPath, std::ios::binary);
        if (objFile.is_open())
        {
            objFile.seekg(0, ios_base::end);
            dwUserFileSize = objFile.tellg();
            objFile.seekg(0, ios_base::beg);
            if (dwUserFileSize > 0)
            {
                pOrgData = new char[dwUserFileSize + 1];
                memset(pOrgData, 0, dwUserFileSize + 1);
                pEncodeData = new char[dwUserFileSize * 2];
                memset(pEncodeData, 0, dwUserFileSize * 2);

                objFile.read(pOrgData, dwUserFileSize);
                base64_encode((unsigned char *)pOrgData, dwUserFileSize, (unsigned char *)pEncodeData);
                strUrlEncode += pEncodeData;
            }
            objFile.close();
        }
    }
    catch (...)
    {
        if (pEncodeData)
        {
            delete[] pEncodeData;
            pEncodeData = NULL;
        }
    }

    if (pOrgData)
    {
        delete[] pOrgData;
        pOrgData = NULL;
    }
    return strUrlEncode;
}

//获取能力云token
bool NengLiYunGetToken(const char* pAppID, const char* pAppSecret, char* pToken)
{
    MHeaderParam mHeaders;
    string strRes = "";

    if (strlen(pAppID) == 0 || strlen(pAppSecret) == 0)
    {
        return false;
    }

    //组POST数据
    Json::Value jsonData;
    jsonData["text"]["clientId"] = pAppID;
    jsonData["text"]["clientSecret"] = pAppSecret;


    if (DoHttpPost(
        c_strIP,//能力云接口IP地址
        c_uPort,//能力云接口端口号
        "/idauth-server/idauth/token/getToken",
        jsonData.toStyledString(),
        mHeaders,
        strRes,
        true,
        true))
    {
        Json::Value jsonRes;
        Json::Reader jReader;
        if (jReader.parse(strRes, jsonRes))
        {
            if (jsonRes["result"].asBool())
            {
                strcpy(pToken, jsonRes["data"]["obj"]["token"].asString().c_str());
                return true;
            }
        }
    }

    return false;
}

//图片一比一接口，如果没证件照可以不传
int NengLiYunFaceCheck(
    const char* pName,
    const char* pCertNum,
    const char* pCertPhoto,
    const char* pFacePhoto,
    const char* pAppID,
    const char* pAppSecret
)
{
    string strUrlPhoto = GetPhotoString(pFacePhoto);
    stringstream sstreamSign;
    string strSign = "", strPostData = "", strBearToken = "";
    char szToken[1024] = { 0 }, szAuth[128] = { 0 };
    MHeaderParam mHead;
    string strRes = "";
    string strName = GBKToUTF8(pName);

    //先获取token,添加到header里面
    if (!NengLiYunGetToken(pAppID, pAppSecret, szToken))
    {
        return emFaceCheckError;
    }
    strBearToken = "Bearer ";
    strBearToken += szToken;
    mHead.insert(make_pair(string("Authorization"), strBearToken));

    //获取照片base64，计算参数MD5签名，组post数据
    Json::Value jsonData;
    if (_access(pCertPhoto, 0) == 0)//图片一比一接口
    {
        string strUrlCert = GetPhotoString(pCertPhoto);

        sstreamSign << "facePhotoBase64=" << strUrlPhoto
            << "&idNumber=" << pCertNum
            << "&idPhotoBase64=" << strUrlCert
            << "&name=" << strName
            << "&" << c_strUserID;

        Md5Encrypt(sstreamSign.str().c_str(), szAuth);

        jsonData["text"]["name"] = strName;
        jsonData["text"]["idNumber"] = pCertNum;
        jsonData["text"]["facePhotoBase64"] = strUrlPhoto;
        jsonData["text"]["idPhotoBase64"] = strUrlCert;
        jsonData["text"]["sign"] = szAuth;
    }
    else//没有证件照的一比一接口
    {
        sstreamSign << "facePhotoBase64=" << strUrlPhoto
            << "&idNumber=" << pCertNum
            << "&name=" << strName
            << "&" << c_strUserID;

        Md5Encrypt(sstreamSign.str().c_str(), szAuth);

        jsonData["text"]["name"] = strName;
        jsonData["text"]["idNumber"] = pCertNum;
        jsonData["text"]["facePhotoBase64"] = strUrlPhoto;
        jsonData["text"]["sign"] = szAuth;
    }

    //发送请求
    if (DoHttpPost(c_strIP,
        c_uPort,
        "/idauth-server/idauth/auth/1to1",
        jsonData.toStyledString(),
        mHead,
        strRes,
        true,
        true))
    {
        //解析结果
        Json::Value jsonRes;
        Json::Reader jReader;
        if (jReader.parse(strRes, jsonRes))
        {
            string strRetCode = jsonRes["code"].asString();
            bool bResult = jsonRes["result"].asBool();
            string strMsg = Utf8ToAnsi(jsonRes["message"].asString().c_str());
            cout << strMsg << endl;
            if (bResult)
            {
                return emFaceCheckPass;
            }
            else
            {
                return emFaceCheckFail;
            }
        }
    }

    return emFaceCheckError;
}

bool DoHttpPost(
    const std::string &strHost,
    unsigned short uPort,
    const std::string &strUrl,
    const std::string &strData,
    const std::map<std::string, std::string> &mapHeaders,
    std::string &strResponse,
    bool bUseSSL /*= false*/,
    bool bUseForm /*= false*/)
{
    try
    {
        HTTPRequest request(HTTPRequest::HTTP_POST, strUrl);
        for (auto iter = mapHeaders.begin(); iter != mapHeaders.end(); iter++)
        {
            request.add(iter->first, iter->second);
        }

        HTTPResponse response;
        HTTPClientSession *pSession = NULL;
        if (bUseSSL)
        {
            //忽略https证书校验错误
            SSLManager::InvalidCertificateHandlerPtr handlerPtr(new AcceptCertificateHandler(false));
            Context::Ptr context = new Context(Context::CLIENT_USE, "");
            SSLManager::instance().initializeClient(nullptr, handlerPtr, context);
            pSession = new HTTPSClientSession(strHost, uPort);
        }
        else
        {
            pSession = new HTTPClientSession(strHost, uPort);
        }

        //超时时间
        pSession->setTimeout(Poco::Timespan(10, 0), Poco::Timespan(10, 0), Poco::Timespan(10, 0));
        if (bUseForm)
        {
            HTMLForm form(HTMLForm::ENCODING_MULTIPART);
            Json::Value jData;
            Json::Reader reader;
            if (reader.parse(strData, jData))
            {
                if (jData.isMember("text"))
                {
                    vector<string> jsonMembers = jData["text"].getMemberNames();
                    for (auto iter = jsonMembers.begin(); iter != jsonMembers.end(); iter++)
                    {
                        form.set(*iter, jData["text"][*iter].asString());
                    }
                }
                if (jData.isMember("file"))
                {
                    vector<string> jsonMembers = jData["file"].getMemberNames();
                    for (auto iter = jsonMembers.begin(); iter != jsonMembers.end(); iter++)
                    {
                        form.addPart(*iter, new FilePartSource(jData["file"][*iter].asString()));
                    }
                }
            }
            else
            {
                strResponse = "参数错误";
            }

            //设置内容长度
            form.prepareSubmit(request);
            request.setContentLength(form.calculateContentLength());
            form.write(pSession->sendRequest(request));
        }
        else
        {
            //设置内容长度
            request.setContentLength((int)strData.length());
            pSession->sendRequest(request) << strData;
        }
        std::istream& rs = pSession->receiveResponse(response);

        if (response.getStatus() != HTTPResponse::HTTP_OK)
        {
            strResponse = response.getReasonForStatus(response.getStatus());
            delete pSession;
            return false;
        }

        std::ostringstream ostr;
        StreamCopier::copyStream(rs, ostr);
        strResponse = ostr.str();

        delete pSession;
        return true;
    }
    catch (Poco::Exception& e)
    {
        strResponse = e.displayText();
        return false;
    }
}


int main()
{
    int ret = NengLiYunFaceCheck("金鑫", "810000199408230021", "cert.jpg", "face.jpg", c_strClientID.c_str(), c_strClientSecret.c_str());
    std::cout << "Face Check Result:" << ret << "\n";
}
